package com.laboratory.miniolearning.service.impl;

import com.laboratory.miniolearning.domin.UploadResult;
import com.laboratory.miniolearning.properties.MinioProperties;
import com.laboratory.miniolearning.service.FileService;
import io.minio.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.time.LocalDate;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class FileServiceImpl implements FileService {

    private final MinioClient minioClient;

    private final MinioProperties minioProperties;

    /**
     * 上传文件
     *
     * @param file
     * @param dir
     * @return
     */
    @Override
    public UploadResult upload(MultipartFile file, String dir) {
        try {
            String bucket = minioProperties.getBucket();
            String objectName = buildObjectName(dir, file.getOriginalFilename());
            String contentType = pickContentType(file.getContentType(), file.getOriginalFilename());
            ObjectWriteResponse objectWriteResponse = minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(objectName)
                            .contentType(contentType)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .build()
            );
            return UploadResult.builder()
                    .bucket(bucket)
                    .objectName(objectName)
                    .contentType(contentType)
                    .size(file.getSize())
                    .etag(objectWriteResponse.etag())
                    .url(minioProperties.getEndpoint() + "/" + bucket + "/" + objectName)
                    .build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 构建对象名
     *
     * @param dir
     * @param fileName
     * @return
     */
    public String buildObjectName(String dir, String fileName) {
        String date = LocalDate.now().toString();
        String ext = safeExt(fileName);
        String base = UUID.randomUUID().toString().replace("-", "");
        String prefix = normalizeDir(dir);
        return (prefix.isEmpty() ? "" : (prefix + "/")) + date + "/" + base + ext;
    }

    /**
     * 获取文件后缀名（text.txt -> .txt）
     *
     * @param fileName
     * @return
     */
    public String safeExt(String fileName) {
        if (fileName == null) {
            return "";
        }
        int pos = fileName.lastIndexOf(".");
        if (pos >= 0 && pos < fileName.length() - 1) {
            return fileName.substring(pos);
        }
        return "";
    }

    /**
     * 规范化文件名
     *
     * @param dir
     * @return
     */
    public String normalizeDir(String dir) {
        if (dir == null) {
            return "";
        }
        String trim = dir.trim();
        while (trim.startsWith("/")) {
            trim = trim.substring(1);
        }
        while (trim.endsWith("/")) {
            trim = trim.substring(0, trim.length() - 1);
        }
        return trim;
    }

    /**
     * 文件类型
     *
     * @param fileName
     * @return
     */
    public String pickContentType(String contentType, String fileName) {
        if (contentType != null && !contentType.isEmpty()) {
            return contentType;
        }
        try {
            if (fileName != null) {
                String probed = Files.probeContentType(new File(fileName).toPath());
                if (probed != null) {
                    return probed;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return "application/octet-stream";
    }

    /**
     * 下载文件
     *
     * @param objectName
     * @return
     */
    public InputStream download(String objectName) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(minioProperties.getBucket())
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 下载文件到指定目录
     *
     * @param objectName
     * @param dest
     */
    public void downloadToFile(String objectName, File dest) {
        try {
            minioClient.downloadObject(
                    DownloadObjectArgs.builder()
                            .bucket(minioProperties.getBucket())
                            .object(objectName)
                            .filename(dest.getAbsolutePath())
                            .build()
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除对象
     *
     * @param objectName
     */
    public void removeObjectName(String objectName) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(minioProperties.getBucket())
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 判断对象是否存在
     *
     * @param objectName
     * @return
     */
    public boolean exists(String objectName) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(minioProperties.getBucket())
                            .object(objectName)
                            .build()
            );
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取文件信息
     *
     * @param objectName
     * @return
     */
    public StatObjectResponse statObject(String objectName) {
        try {
            return minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(minioProperties.getBucket())
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            return null;
        }
    }

}
